﻿using LightCAD.Core.Elements;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using LightCAD.MathLib;
using System.Linq;
using System.Security.AccessControl;

namespace LightCAD.Core
{
    public class LcAxisCircle : LcElement
    {
        public LcCircle LcCircle { get; set; }
        public LcText LcText { get; set; }
        public LcAxisCircle() { 
        
            this.Type = BuiltinElementType.AxisCircle;

        }
        public override LcElement Clone()
        {
            throw new NotImplementedException();
        }
        public override Box2 GetBoundingBox()
        {
            var box = Box2.Empty;
            box.Union(this.LcCircle.GetBoundingBox());
            return box;
        }
        public override Box2 GetBoundingBox(Matrix3 matrix)
        {
            var box = Box2.Empty;
            box.Union(this.LcCircle.GetBoundingBox(matrix));
            return box;
        }
        public override LcElement ApplyMatrix(Matrix3 matrix3)
        {
            this.LcCircle.ApplyMatrix(matrix3);
            this.LcText.ApplyMatrix(matrix3);
            return this;
        }
        public override bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            Box2 thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                return false;
            }
            if (this.LcCircle.IntersectWithBox(testPoly))
            {
                return true;
            }
            if (this.LcText.IntersectWithBox(testPoly))
            {
                return true;
            }

            if (intersectChildren == null) return false;
            //任意子元素被相交，则元素被相交
            return intersectChildren.Count > 0;
        }

        public override bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            Box2 thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                return false;
            }
            if (this.LcCircle.IncludedByBox(testPoly))
            {
                return true;
            }
            if (this.LcText.IncludedByBox(testPoly))
            {
                return true;
            }
            if (includedChildren == null) return true;
            //所有子元素都被包含，则本元素被包含
            return false;
        }
        public override void WriteProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            LcCircle.WriteProperties(writer, soptions);
            LcText.WriteProperties(writer, soptions);
        }
        public override void ReadProperties(ref JsonElement jele)
        {
            LcText = this.Document.CreateObject<LcText>();
            LcCircle = this.Document.CreateObject<LcCircle>();
            LcText.ReadProperties(ref jele);
            LcCircle.ReadProperties(ref jele);
        }

    }
    public class LcAxisLine : LcElement
    {
        public LcAxisLine()
        {
            StartLeadLines = new List<LcLine>();
            EndLeadLines = new List<LcLine>();
            StartCircleHandle = new List<LcAxisCircle>();
            EndCircleHandle = new List<LcAxisCircle>();
            this.Type = BuiltinElementType.AxisLine;

        }
        public LcLine AxisLine { get; set; }
        public List<LcLine> StartLeadLines { get; set; }
        public Vector2 Vector { get { return (AxisLine.Start - AxisLine.End).Normalize(); } }
        public List<LcLine> EndLeadLines { get; set; }
        public List<LcAxisCircle> StartCircleHandle { get; set; }
        public List<LcAxisCircle> EndCircleHandle { get; set; }
        public override LcElement Clone()
        {
            LcAxisLine lcAxisLine = new LcAxisLine();
            return lcAxisLine;
        }

        public override Box2 GetBoundingBox()
        {
            var box = Box2.Empty;
            box.Union(this.AxisLine.GetBoundingBox());

            foreach (var ele in StartLeadLines)
            {
                box.Union(ele.GetBoundingBox());
            }
            foreach (var ele in EndLeadLines)
            {
                box.Union(ele.GetBoundingBox());
            }
            foreach (var ele in StartCircleHandle)
            {
                box.Union(ele.GetBoundingBox());
            }
            foreach (var ele in EndCircleHandle)
            {
                box.Union(ele.GetBoundingBox());
            }
            return box;
        }
        public override Box2 GetBoundingBox(Matrix3 matrix)
        {
            var box = Box2.Empty;
            box.Union(this.AxisLine.GetBoundingBox(matrix));
            foreach (var ele in this.EndLeadLines)
            {
                box.Union(ele.GetBoundingBox(matrix));
            }
            foreach (var ele in StartLeadLines)
            {
                box.Union(ele.GetBoundingBox(matrix));
            }
            foreach (var ele in EndLeadLines)
            {
                box.Union(ele.GetBoundingBox(matrix));
            }
            foreach (var ele in StartCircleHandle)
            {
                box.Union(ele.GetBoundingBox(matrix));
            }
            foreach (var ele in EndCircleHandle)
            {
                box.Union(ele.GetBoundingBox(matrix));
            }
            return box;
        }


        public override bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            Box2 thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                return false;
            }
            if (this.AxisLine.IntersectWithBox(testPoly))
            {
                return true;
            }

            foreach (var ele in this.StartLeadLines)
            {
                if (intersectChildren == null)
                {
                    //如果不需要计算子元素包含情况，有一个子元素没包含，则本元素不被包含
                    if (!ele.IntersectWithBox(testPoly))
                    {
                        return false;
                    }
                }
            }
            foreach (var ele in this.EndLeadLines)
            {
                if (intersectChildren == null)
                {
                    //如果不需要计算子元素包含情况，有一个子元素没包含，则本元素不被包含
                    if (!ele.IntersectWithBox(testPoly))
                    {
                        return false;
                    }
                }
            }
            foreach (var ele in this.StartCircleHandle)
            {
                if (intersectChildren == null)
                {
                    //如果不需要计算子元素包含情况，有一个子元素没包含，则本元素不被包含
                    if (!ele.IntersectWithBox(testPoly))
                    {
                        return false;
                    }
                }
            }
            foreach (var ele in this.EndLeadLines)
            {
                if (intersectChildren == null)
                {
                    //如果不需要计算子元素包含情况，有一个子元素没包含，则本元素不被包含
                    if (!ele.IntersectWithBox(testPoly))
                    {
                        return false;
                    }
                }
            }
            if (intersectChildren == null) return false;
            //任意子元素被相交，则元素被相交
            return intersectChildren.Count > 0;
        }

        public override bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            Box2 thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                return false;
            }
            if (this.AxisLine.IncludedByBox(testPoly))
            {
                return true;
            }
            foreach (var ele in this.StartLeadLines)
            {
                if (includedChildren == null)
                {
                    //如果不需要计算子元素包含情况，有一个子元素没包含，则本元素不被包含
                    if (!ele.IncludedByBox(testPoly))
                    {
                        return false;
                    }
                }
            }
            foreach (var ele in this.EndLeadLines)
            {
                if (includedChildren == null)
                {
                    //如果不需要计算子元素包含情况，有一个子元素没包含，则本元素不被包含
                    if (!ele.IncludedByBox(testPoly))
                    {
                        return false;
                    }
                }
            }
            foreach (var ele in this.StartCircleHandle)
            {
                if (includedChildren == null)
                {
                    //如果不需要计算子元素包含情况，有一个子元素没包含，则本元素不被包含
                    if (!ele.IncludedByBox(testPoly))
                    {
                        return false;
                    }
                }
            }
            foreach (var ele in this.EndLeadLines)
            {
                if (includedChildren == null)
                {
                    //如果不需要计算子元素包含情况，有一个子元素没包含，则本元素不被包含
                    if (!ele.IncludedByBox(testPoly))
                    {
                        return false;
                    }
                }
            }
            if (includedChildren == null) return true;
            //所有子元素都被包含，则本元素被包含
            return false;
        }
        public override LcElement ApplyMatrix(Matrix3 matrix3)
        {
            this.AxisLine.ApplyMatrix(matrix3);
            foreach (var ele in this.StartLeadLines)
            {
                ele.ApplyMatrix(matrix3);
            }
            foreach (var ele in this.EndLeadLines)
            {
                ele.ApplyMatrix(matrix3);
            }
            foreach (var ele in this.StartCircleHandle)
            {
                ele.ApplyMatrix(matrix3);
            }
            foreach (var ele in this.EndCircleHandle)
            {
                ele.ApplyMatrix(matrix3);
            }
            return this;
        }
        public override Vector2 LineCrossLine(Line2d line2D)  //直线与直线的交点 重载调用
        {
            Vector2 point = Line2d.GetCrossVector(new Line2d(this.AxisLine.Start, this.AxisLine.End), line2D);
            return point;
        }
        public override List<Vector2> GetCrossVectorByLine(Line2d lcLine)
        {
            List<Vector2> vector2s = new List<Vector2>
            {
                LineCrossLine(lcLine)
            };
            return vector2s;

        }
        public void Set(Vector2 start = null, Vector2 end = null, bool fireChangedEvent = true)
        {
            //PropertySetter:Start,End
            if (!fireChangedEvent)
            {
                if (start != null) this.AxisLine.Start = start;
                if (end != null) this.AxisLine.End = end;
            }
            else
            {
                bool chg_start = (start != null && start != this.AxisLine.Start);
                if (chg_start)
                {
                    OnPropertyChangedBefore(nameof(this.AxisLine.Start), this.AxisLine.Start, start);
                    var oldValue = this.AxisLine.Start;
                    Vector2 offPoint = start - this.AxisLine.Start;
                    Matrix3 matrix3 = Matrix3.GetTranslate(offPoint);
                    //double angle = Math.Atan2(this.AxisLine.End.Y - this.AxisLine.Start.Y, this.AxisLine.End.X - this.AxisLine.Start.X);
                    //matrix3 = matrix3 * Matrix3.Rotate(angle, this.AxisLine.Start);
                    this.AxisLine.Start = start;
                    for (int i = 0; i < this.StartLeadLines.Count; i++)
                    {
                        var lcLine = this.StartLeadLines[i];
                        lcLine = lcLine.ApplyMatrix(matrix3) as LcLine;
                        lcLine.ResetBoundingBox();

                    }
                    for (int i = 0; i < this.StartCircleHandle.Count; i++)
                    {
                        var lcAxisCircle = this.StartCircleHandle[i];
                        lcAxisCircle = lcAxisCircle.ApplyMatrix(matrix3) as LcAxisCircle;
                        lcAxisCircle.ResetBoundingBox();

                    }
                    OnPropertyChangedAfter(nameof(this.AxisLine.Start), oldValue, this.AxisLine.Start);
                }
                bool chg_end = (end != null && end != this.AxisLine.End);
                if (chg_end)
                {
                    OnPropertyChangedBefore(nameof(this.AxisLine.End), this.AxisLine.End, end);
                    var oldValue = this.AxisLine.End;
                    Vector2 offPoint = end - this.AxisLine.End;
                    Matrix3 matrix3 = Matrix3.GetTranslate(offPoint);
                    //double angle = Math.Atan2(this.AxisLine.End.Y - this.AxisLine.Start.Y, this.AxisLine.End.X - this.AxisLine.Start.X);
                    //matrix3 = matrix3 * Matrix3.Rotate(angle, this.AxisLine.End);

                    this.AxisLine.End = end;
                    //matrix3 = matrix3 * Matrix3.Rotate(offAngle, this.AxisLine.Start);
                    for (int i = 0; i < this.EndLeadLines.Count; i++)
                    {
                        var lcLine = this.EndLeadLines[i];
                        lcLine = lcLine.ApplyMatrix(matrix3) as LcLine;
                        lcLine.ResetBoundingBox();

                    }
                    for (int i = 0; i < this.EndCircleHandle.Count; i++)
                    {
                        var lcAxisCircle = this.EndCircleHandle[i];
                        lcAxisCircle = lcAxisCircle.ApplyMatrix(matrix3) as LcAxisCircle;
                        lcAxisCircle.ResetBoundingBox();

                    }
                    OnPropertyChangedAfter(nameof(this.AxisLine.End), oldValue, this.AxisLine.End);
                    this.ResetBoundingBox();
                    this.AxisLine.ResetBoundingBox();

                }
            }
        }

        public override void WriteProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            AxisLine.WriteProperties(writer, soptions);

            writer.WritePropertyName(nameof(StartLeadLines));
            writer.WriteStartArray();
            foreach (var subEle in this.StartLeadLines)
            {
                writer.WriteElementObject(subEle, soptions);
            }
            writer.WriteEndArray();

            writer.WritePropertyName(nameof(EndLeadLines));
            writer.WriteStartArray();
            foreach (var subEle in this.EndLeadLines)
            {
                writer.WriteElementObject(subEle, soptions);
            }
            writer.WriteEndArray();


            writer.WritePropertyName(nameof(StartCircleHandle));
            writer.WriteStartArray();

            foreach (var subEle in this.StartCircleHandle)
            {
                writer.WriteElementObject(subEle, soptions);
            }
            writer.WriteEndArray();

            writer.WritePropertyName(nameof(EndCircleHandle));
            writer.WriteStartArray();
            foreach (var subEle in this.EndCircleHandle)
            {
                writer.WriteElementObject(subEle, soptions);
            }
            writer.WriteEndArray();

        }
        public override void ReadProperties(ref JsonElement jele)
        {
            AxisLine = this.Document.CreateObject<LcLine>();
            AxisLine.ReadProperties(ref jele);
            var arr = jele.GetProperty(nameof(StartLeadLines)).EnumerateArray().ToArray();
            for (var i = 0; i < arr.Length; i++)
            {
                var ele = arr[i].ReadElementObject(this.Document);
                this.StartLeadLines.Add(ele as LcLine);
            }
            arr = jele.GetProperty(nameof(EndLeadLines)).EnumerateArray().ToArray();
            for (var i = 0; i < arr.Length; i++)
            {
                var ele = arr[i].ReadElementObject(this.Document);
                this.EndLeadLines.Add(ele as LcLine);
            }
            arr = jele.GetProperty(nameof(StartCircleHandle)).EnumerateArray().ToArray();
            for (var i = 0; i < arr.Length; i++)
            {
                var ele = arr[i].ReadElementObject(this.Document);
                this.StartCircleHandle.Add(ele as LcAxisCircle);
            }
            arr = jele.GetProperty(nameof(EndCircleHandle)).EnumerateArray().ToArray();
            for (var i = 0; i < arr.Length; i++)
            {
                var ele = arr[i].ReadElementObject(this.Document);
                this.EndCircleHandle.Add(ele as LcAxisCircle);
            }


        }
    }

    public class LcAxisLineCollection : KeyedCollection<long, LcAxisLine>
    {
        public LcAxisLineCollection() { }

        protected override long GetKeyForItem(LcAxisLine item)
        {
            return item.Id;
        }
    }
}